package com.bb.flow.service.imp.executor.node;

import com.bb.flow.api.FlowNode;
import com.bb.flow.api.NotifyExecuteResult;
import com.bb.flow.api.entries.FlowContext;
import com.bb.flow.api.type.ExecuteState;
import com.bb.flow.config.AbstractConfigNode;
import com.bb.flow.config.FlowInfo;
import com.bb.flow.config.enumtype.NodeType;
import com.bb.flow.config.enumtype.WaitType;
import com.bb.flow.config.node.FunctionConfigNode;
import com.bb.flow.service.api.NodeStateCallback;
import com.bb.flow.service.imp.AbstractNodeExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author bigbeard
 * 功能节点执行器
 */
public class FunctionNodeExecutor extends AbstractNodeExecutor {
    private final static Logger logger = LoggerFactory.getLogger(FunctionNodeExecutor.class);

    private static final String EXECUTE_NODE_THREAD_NAME = "execute-function-node-thread-%s";


    @Override
    protected String getThreadName() {
        return EXECUTE_NODE_THREAD_NAME;
    }


    /**
     * 执行功能节点,开始调用功能节点的功能,并等待功能节点反馈执行结果
     *
     * @param node              执行节点
     * @param flowInfo          流程信息
     * @param nodeStateCallback 执行状态回调通知接口
     */
    @Override
    protected void executeNode(
            AbstractConfigNode node, FlowInfo flowInfo
            , NodeStateCallback nodeStateCallback) {
        if (canExecute(node)) {
            return;
        }
        this.setNodeStateCallback(nodeStateCallback);
        this.setCurrentFlowInfo(flowInfo);
        this.setCurrentNode(node);
        notifyExecuteState(ExecuteState.EXECUTING);
        FunctionConfigNode functionConfigNode = (FunctionConfigNode) node;
        FlowNode flowNode = functionConfigNode.getFlowNode();
        if (null == flowNode) {
            logger.error("流程模版[{}]中的节点[{}]业务功能节点为null,无法执行", flowInfo.getFlowTemplateName(), node.getNodeKey());
            notifyExecuteState(ExecuteState.EXCEPTION);
            return;
        }

        FlowContext flowContext = flowInfo.getFlowContext();
        flowContext.setNodeKey(node.getNodeKey());

        flowNode.setFlowNodeData(flowContext);

        if (node.getWaitType() == WaitType.WAIT_TIME && node.getWaitTime() > 0) {
            if (null == flowNode.getExecuteOverNotify()) {
                flowNode.setExecuteOverNotify(getNotifyExecuteResult(flowNode));
            }
            Future<?> future = execute(flowNode);
            try {
                future.get(node.getWaitTime(), TimeUnit.SECONDS);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                //此处可能出现两次通知
                flowNode.setExecuteOverNotify(null);
                String parentFlowName = getParentFlowName(currentFlowInfo);
                if (!parentFlowName.isEmpty()) {
                    logger.error("flowDriver:在执行流程:父流程模版[{}],子流程模版[{}]中的节点[{}]的业务操作过程中发生异常",
                            parentFlowName, currentFlowInfo.getFlowTemplateName(), getCurrentNode().getNodeKey(), e);
                } else {
                    logger.error("flowDriver:在执行流程模版[{}]中的节点[{}]的业务操作过程中发生异常",
                            currentFlowInfo.getFlowTemplateName(), getCurrentNode().getNodeKey(), e);
                }
                notifyExecuteState(ExecuteState.TIME_OUT);
            }
        } else if (node.getWaitType() == WaitType.NOT_WAIT) {
            execute(flowNode);
            notifyExecuteState(ExecuteState.FINISH);
        } else {//一直等
            if (null == flowNode.getExecuteOverNotify()) {
                flowNode.setExecuteOverNotify(getNotifyExecuteResult(flowNode));
            }
            execute(flowNode);
        }
    }

    private NotifyExecuteResult getNotifyExecuteResult(FlowNode flowNode) {
        return (result) -> {
            flowNode.setExecuteOverNotify(null);
            if (result) {
                notifyExecuteState(ExecuteState.FINISH);
            } else {
                notifyExecuteState(ExecuteState.EXCEPTION);
            }
        };
    }

    /**
     * 节点类型
     *
     * @return 节点类型
     */
    @Override
    protected NodeType getAllowExecuteNodeType() {
        return NodeType.FUNCTION_NODE;
    }


    /**
     * 执行当前节点操作
     *
     * @param flowNode 流程节点对象
     * @return 任务执行future
     */
    private Future<?> execute(FlowNode flowNode) {
        return executorService.submit(() -> {
                    try {
                        flowNode.execute();
                    } catch (Exception e) {
                        String parentFlowName = getParentFlowName(currentFlowInfo);
                        if (!parentFlowName.isEmpty()) {
                            logger.error("flowDriver:在执行流程:父流程模版[{}]子流程模版[{}]中的节点[{}]的业务操作过程中发生异常",
                                    parentFlowName, currentFlowInfo.getFlowTemplateName(), getCurrentNode().getNodeKey(), e);
                        } else {
                            logger.error("flowDriver:在执行流程模版[{}]中的节点[{}]的业务操作过程中发生异常",
                                    currentFlowInfo.getFlowTemplateName(), getCurrentNode().getNodeKey(), e);
                        }
                        notifyExecuteState(ExecuteState.EXCEPTION);
                    }
                }
        );
    }

    private String getParentFlowName(FlowInfo flowInfo) {
        StringBuilder parent = new StringBuilder();
        FlowInfo tmp = flowInfo.getParentFlow();
        while (tmp != null) {
            parent.append(tmp.getFlowTemplateName());
            tmp = tmp.getParentFlow();
            if (null != tmp) {
                parent.append("/");
            }
        }
        return parent.toString();
    }
}
